#!/usr/bin/env python
import os
import json
import time
from datetime import datetime
from requests import get, post

from squirrel_core.commons.utils.logger import Logging
from squirrel_core.commons.utils import Singleton
from squirrel_core.commons.utils.get_config import get_config


class Proxy(metaclass=Singleton):
    def __init__(self, proxy_max_use=0):
        self.logger = Logging()
        self.req_proxy_num = 10
        self.proxy = None
        self.error_code_list = [10060, 403, 404, 502, 407, 111, 110, 10061]
        self.max_proxy_times = proxy_max_use if proxy_max_use else 1
        self.proxy_pool = []
        self.effective_used_proxy = []
        self.invalid_used_proxy = []
        self.base_config = get_config(sections="base")
        self.dmdaili_url = self.base_config.get("dmdaili_url", "")
        self.huashengdaili_url = self.base_config.get("huashengdaili_url", "")
        self.xiecaiyun_url = self.base_config.get("xiecaiyun_url", "")
        self.wx_url = self.base_config.get("wx_url", "")

    @property
    def get_proxy(self):
        self.proxy = self._get_proxy(proxy_change=False, proxy_black_list=[])
        return self.proxy
    
    def get_proxy_from_http(self)->list:
        self.logger.info("开始申请代理")
        try:
            ret = get(self.xiecaiyun_url, timeout=10)
            ip_list = json.loads(ret.text).get("result", [])
        except Exception as e:
            self.logger.error(f"请求协采云代理异常, 原因: {e}")
            # self.post_message("协采云代理接口访问异常\n爬虫已自动切换至多米代理")
            try:
                ret = get(self.dmdaili_url, timeout=10)
                ip_list = json.loads(ret.text).get("data", [])
            except Exception as e:
                self.logger.error(f"请求多米代理异常, 原因: {e}")
                # self.post_message("多米代理接口访问异常\n爬虫已自动切换至花生代理")
                try:
                    ret = get(self.huashengdaili_url, timeout=10)
                    ip_list = json.loads(ret.text).get("list", [])
                except Exception as e:
                    self.logger.error(f"请求花生代理异常, 原因: {e}")
                    self.post_message("爬虫已无代理可用，请及时处理！")
                    return []
        if not ip_list:
            # self.post_message("协采云代理返回空，将切换至多米代理！")
            try:
                ret = get(self.dmdaili_url, timeout=10)
                ip_list = json.loads(ret.text).get("data", [])
            except Exception as e:
                self.logger.error(f"请求多米代理异常, 原因: {e}")
                # self.post_message("多米代理接口访问异常\n爬虫已自动切换至花生代理")
                try:
                    ret = get(self.huashengdaili_url, timeout=10)
                    ip_list = json.loads(ret.text).get("list", [])
                except Exception as e:
                    self.logger.error(f"请求花生代理异常, 原因: {e}")
                    self.post_message("爬虫已无代理可用，请及时处理！")
                    return []
        if not ip_list:
            # self.post_message("多米代理返回空，将切换至花生代理！")
            try:
                ret = get(self.huashengdaili_url, timeout=10)
                ip_list = json.loads(ret.text).get("list", [])
            except Exception as e:
                self.logger.error(f"请求花生代理异常, 原因: {e}")
                self.post_message("爬虫已无代理可用，请及时处理！")
                return []
        if ip_list:
            ret_list = self.split_proxy(ip_list)
            self.logger.info('请求代理成功')
            return ret_list
        else:
            self.post_message("获取IP失败，请及时确认是否欠费或联系供应商！")
            return []

    def split_proxy(self, ip_list):
        ret_list = []
        for proxy in ip_list:
            try:
                ip = proxy.get("ip", "") or proxy.get("sever", "")
                port = proxy.get("port")
                proxy_str = f"{ip}:{port}"
                format_str = "%Y/%m/%d %H:%M:%S"
                if proxy.get("ltime", ""):
                    dead_time = int(proxy.get("ltime", ""))
                else:
                    end_time = proxy.get("endtime", "") or proxy.get("expire_time", "").replace("-", "/")
                    time_tuple = time.strptime(end_time, format_str)
                    dead_time = int(time.mktime(time_tuple))
                if proxy.get("ltime", ""):
                    # proxy_str = f"HxUx3cAu:3KpZTYNS@{proxy_str}"
                    proxy_str = f"hO09CYtU:wD7izotM@{proxy_str}"
                ret_list.append({'proxy': proxy_str, 'ip': ip, 'port': port, 'timeout': dead_time})
            except Exception as e:
                self.logger.info(f'代理解析失败,错误信息:{e}')
        return ret_list

    def _select_proxy(self):
        try:
            for proxy in self.proxy_pool:
                use_too_much = int(proxy.get('local_times', 0)) >= self.max_proxy_times
                status_code_not_allowed = proxy.get('last_state', 200) in self.error_code_list
                is_timeout = isinstance(proxy.get('timeout'), (int, float, str)) and int(proxy.get('timeout')) - 10 < time.time()
                if any([use_too_much, status_code_not_allowed, is_timeout]):
                    if proxy not in self.invalid_used_proxy + self.effective_used_proxy:
                        self.logger.warning(f"代理信息不可用:{proxy.get('proxy')}, 超过连续使用次数:{use_too_much},"
                                            f" 状态码不可用:{status_code_not_allowed}, 过期:{is_timeout}")
                        self.invalid_used_proxy.append(proxy)
                    continue
                else:
                    proxy['local_times'] = int(proxy.get('local_times', 0)) + 1
                    proxy['used_count'] = int(proxy.get('used_count', 0)) + 1
                    proxy['last_time'] = time.time()
                    self.effective_used_proxy.append(proxy)
                    self.logger.info(f"成功获取可用代理: {proxy}")
                    return proxy
            return None
        except Exception as e:
            self.logger.error(f"从代理池选择代理出错:{e}")
            return None
    
    def set_state(self, state_code, ip):
        for proxy in self.proxy_pool:
            if isinstance(ip, dict):
                if ip.get('proxy', '') == proxy.get('proxy', ''):
                    proxy['last_state'] = int(state_code)
                    self.logger.info(f"设置代理: {proxy.get('proxy', '')} 状态: {state_code}成功")
                    break
            else:
                if ip == proxy.get('proxy'):
                    proxy['last_state'] = int(state_code)
                    self.logger.info(f"设置代理:{proxy.get('proxy', '')} 状态:{state_code}成功")
                    break
    
    def _create_proxy(self):
        tm_proxy_pool = list()
        _sleep_time = 10
        while True:
            try:
                proxy_list = self.get_proxy_from_http()
                if not proxy_list:
                    self.logger.info(f'申请代理为空, 休眠{_sleep_time}秒重试')
                    time.sleep(_sleep_time)
                    continue
                for proxy in proxy_list:
                    if isinstance(proxy, str):
                        proxy = eval(proxy)
                    proxy['local_times'] = 0
                    tm_proxy_pool.append(proxy)
                return tm_proxy_pool
            except Exception as e:
                self.logger.error(f"申请代理异常，原因:{e}, 休眠{_sleep_time}秒重试")
    
    def _get_proxy(self, proxy_change, proxy_black_list)->dict:
        try:
            proxy = self._select_proxy()
            while True:
                if proxy is None or proxy_change or proxy_black_list:
                    self.proxy_pool = self._create_proxy()
                    proxy = self._select_proxy()
                    if proxy:
                        break
                else:
                    break
            self.logger.info(f"通过_get_proxy获取代理成功: {proxy}")
            return proxy
        except Exception as e:
            self.logger.warning(e)
            return {}

    def post_message(self, msg):
        current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        payload = {
            "msgtype": "text",
            "text": {
                "content": f"【代理服务提醒：{current_time}】\n{msg}",
                "mentioned_list": ["@all"]
            }
        }
        try:
            post(self.wx_url, headers={"Content-Type": "text/plain"}, data=json.dumps(payload), timeout=10)
        except Exception as e:
            self.logger.error(f"代理异常监控信息发送失败, 原因: {e}")

if __name__ == '__main__':
    proxy_instance = Proxy(4)
    proxy = proxy_instance._get_proxy(True, [])
    pass
